package com.yl.common.utils;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import lombok.Getter;
import lombok.SneakyThrows;
import org.springframework.util.CollectionUtils;

import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 树的处理
 */
public class TreeUtils {
    @Getter
    public enum TreeFieldEnum{
        ID("id","主键id字段"),
        PARENT_ID("parentId","父类id字段"),
        CHILD("child","子类字段");

        private final String code;
        private final String name;

        TreeFieldEnum(String code,String name ){
            this.code = code;
            this.name = name;
        }
    }

    /**
     * 构件树 从子节点到根节点
     *
     * @param allNode          构件树的节点数据
     * @param idFieldStr       节点主键字段
     * @param childFieldStr    子类字段
     * @param parentIdFieldStr 父类id字段
     * @param isReturnRootNode 是否只返回根节点 默认返回
     */
    public static <T> List<T> createTree(
            List<T> allNode,
            String idFieldStr,
            String parentIdFieldStr,
            String childFieldStr,
            Boolean isReturnRootNode
    ) {
        if (CollUtil.isEmpty(allNode)) {
            return Collections.emptyList();
        }

        //获取class
        Class<?> aClass = allNode.get(0).getClass();
        //获取字段Field
        Map<String, Field> fieldMap = getAllFieldMap(aClass);

        //判断字段在类中是否存在
        Field idField = fieldMap.get(idFieldStr);
        Field parentIdField = fieldMap.get(parentIdFieldStr);
        Field childField = fieldMap.get(childFieldStr);

        if (idField == null) {
            throw new IllegalArgumentException("找不到 " + idFieldStr + "属性");
        }
        if (parentIdField == null) {
            throw new IllegalArgumentException("找不到 " + parentIdFieldStr + "属性");
        }
        if (childField == null) {
            throw new IllegalArgumentException("找不到 " + childFieldStr + "属性");
        }

        Set<Object> ids = allNode.stream()
                .map(i -> {
                    try {
                        return idField.get(i);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                    return null;
                })
                .collect(Collectors.toSet());

        List<T> rootNode = new ArrayList<>();
        for (T i : allNode) {
            try {
                //获取当前i节点的父类id
                Object parentId = parentIdField.get(i);
                //判断是否是根节点 如果是 添加到rootNode中
                // if(parentId.equals(rootNodeParentIdValue)){
                //     rootNode.add(i);
                //     continue;
                // }
                //根节点的父类id一般是0或是null或是空 所以在ids中肯定不包含
                if (!ids.contains(parentId)) {
                    rootNode.add(i);
                    continue;
                }

                //如果不是根节点 循环所有节点 查找父类节点
                for (T j : allNode) {

                    //获取j这个父节点的字节点数组数据
                    List<T> childList = (List<T>) childField.get(j);
                    //如果是空的先进行初始化
                    if (Objects.isNull(childList)) {
                        childList = new ArrayList<>();
                        childField.set(j, childList);
                    }

                    //获取当前j节点的主键id
                    Object id = idField.get(j);
                    //当前的j节点不是当前i节点的父节点 返回进行下一次循环
                    if (!parentId.equals(id)) {
                        continue;
                    }

                    //把节点i添加到节点j的子节点数组中
                    childList.add(i);
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }

        if (Objects.isNull(isReturnRootNode) || isReturnRootNode) {
            return rootNode;
        }

        return allNode;
    }

    /**
     * 获取所有节点的父节点
     *
     * @param idFieldStr           节点主键字段 例如id
     * @param parentIdFieldStr     节点父节点字段 例如parentId
     * @param allNode              所有节点数据数组数据
     * @param isIncludeCurrentNode 是否包含当前节点 默认false
     * @param sortType             父节点排序方式 true表示父节点到子节点 false表示子节点到父节点  默认false
     * @return Map<Object, List<T>> key为节点id value为父类数据
     */
    public static <T> Map<Object, List<T>> getAllNodeParentNode(
            String idFieldStr,
            String parentIdFieldStr,
            List<T> allNode,
            Boolean isIncludeCurrentNode,
            Boolean sortType

    ) throws IllegalAccessException {
        if (CollectionUtils.isEmpty(allNode)) {
            return new HashMap<>();
        }

        //获取class
        Class<?> aClass = allNode.get(0).getClass();
        //获取字典Field
        Map<String, Field> fieldMap = getAllFieldMap(aClass);
        //获取字段
        Field idField = fieldMap.get(idFieldStr);
        Field parentIdField = fieldMap.get(parentIdFieldStr);

        if (idField == null) {
            throw new IllegalArgumentException("找不到 " + idFieldStr + "属性");
        }
        if (parentIdField == null) {
            throw new IllegalArgumentException("找不到 " + parentIdFieldStr + "属性");
        }

        Map<Object, T> nodeMap = new HashMap<>();
        for (T i : allNode) {
            //idField.get(i)表示从对象i中获取指定字段的值
            nodeMap.put(idField.get(i), i);
        }

        Map<Object, List<T>> allParentMap = new HashMap<>();
        for (T i : allNode) {
            Object id = idField.get(i);
            Object parentId = parentIdField.get(i);

            List<T> list = new ArrayList<>();
            if (!Objects.isNull(isIncludeCurrentNode) && isIncludeCurrentNode) {
                list.add(i);
            }

            while (true) {
                T t = nodeMap.get(parentId);
                if (Objects.isNull(t)) {
                    break;
                }
                list.add(t);
                parentId = parentIdField.get(t);
            }
            allParentMap.put(id, list);
        }

        if (!Objects.isNull(sortType) && sortType) {
            for (Object i : allParentMap.keySet()) {
                List<T> list = allParentMap.get(i);
                Collections.reverse(list);
                allParentMap.put(i, list);
            }
        }

        return allParentMap;
    }

    /**
     * 获取指定节点id（节点id数组） 相关联的所有父类对象 包含指定的节点对象
     *
     * @param idFieldStr 节点主键字段 例如id
     * @param parentIdFieldStr 节点父节点字段 例如parentId
     * @param nodeMap 所有节点组装的map数据 key为节点主键id value为节点对象
     * @param ids 指定节点主键数组
     * @return List<T>
     */
    @SneakyThrows
    public static <T,K> List<T> getParentByIds(
            String idFieldStr,
            String parentIdFieldStr,
            Map<K, T> nodeMap,
            List<K> ids
    ){
        if(
            StrUtil.isBlank(idFieldStr)||
            StrUtil.isBlank(parentIdFieldStr)||
            MapUtil.isEmpty(nodeMap)||
            CollectionUtils.isEmpty(ids)
        ){
            throw new IllegalArgumentException("参数idFieldStr、parentIdFieldStr、nodeMap、ids都不能为空");
        }

        //获取class
        Class<?> aClass = nodeMap.get(ids.get(0)).getClass();
        //获取字段Field
        Map<String, Field> fieldMap = getAllFieldMap(aClass);

        //判断字段在类中是否存在
        Field idField = fieldMap.get(idFieldStr);
        Field parentIdField = fieldMap.get(parentIdFieldStr);

        if (idField == null) {
            throw new IllegalArgumentException("找不到 " + idFieldStr + "属性");
        }
        if (parentIdField == null) {
            throw new IllegalArgumentException("找不到 " + parentIdFieldStr + "属性");
        }

        Set<T> nodeSet = new HashSet<>();
        for (K i : ids) {
            if(!nodeMap.containsKey(i)){
                continue;
            }
            T t = nodeMap.get(i);
            Object parentId = parentIdField.get(t);

            nodeSet.add(t);

            while (true) {
                T parentT = nodeMap.get(parentId);
                if (Objects.isNull(parentT)) {
                    break;
                }
                nodeSet.add(parentT);
                parentId = parentIdField.get(parentT);
            }
        }
        return new ArrayList<>(nodeSet);
    }

    /**
     * 获取当前节点所有父节点id 不包含当前节点的id
     *
     * @param nodeMap               节点数据 key为节点主键id值 value是当前节点
     * @param t                     当前节点
     * @param idFieldStr            节点主键字段 例如id
     * @param parentIdFieldStr      节点父节点字段 例如parentId
     * @param rootNodeParentIdValue 根节点的父节点值 例如根节点的父节点id值一般为空、null、0
     */
    public static <T> String getCurrentNodeParentIds(
            Map<Object, T> nodeMap,
            T t,
            String idFieldStr,
            String parentIdFieldStr,
            Object rootNodeParentIdValue

    ) throws IllegalAccessException {
        //获取class
        Class<?> aClass = t.getClass();
        //获取字典Field
        Map<String, Field> fieldMap = getAllFieldMap(aClass);
        //获取字段
        Field idField = fieldMap.get(idFieldStr);
        Field parentIdField = fieldMap.get(parentIdFieldStr);

        if (idField == null) {
            throw new IllegalArgumentException("找不到 " + idFieldStr + "属性");
        }
        if (parentIdField == null) {
            throw new IllegalArgumentException("找不到 " + parentIdFieldStr + "属性");
        }

        Object id = idField.get(t);
        Object parentId = parentIdField.get(t);
        String allParentIds = parentId.toString();
        while (true) {
            T t2 = nodeMap.get(parentId);
            if (Objects.isNull(t2)) {
                break;
            }
            parentId = parentIdField.get(t2);
            allParentIds = parentId + "," + allParentIds;
        }

        String[] split = allParentIds.split(",");
        if (!split[0].equals(rootNodeParentIdValue.toString())) {
            throw new RuntimeException(idFieldStr + "为" + id + "的节点，所有的父节点id有缺失");
        }

        return allParentIds;
    }

    /**
     * 获取所有节点 每一个节点的所有父节点id 不包含当前节点的id
     *
     * @param allNode               所有节点数据
     * @param idFieldStr            节点主键字段 例如id
     * @param parentIdFieldStr      节点父节点字段 例如parentId
     * @param rootNodeParentIdValue 根节点的父节点值 例如根节点的父节点id值一般为空、null、0
     */
    public static <T> Map<Object, String> getAllParentIdsByAllNode(
            List<T> allNode,
            String idFieldStr,
            String parentIdFieldStr,
            Object rootNodeParentIdValue

    ) throws IllegalAccessException {
        if (CollectionUtils.isEmpty(allNode)) {
            return new HashMap<>();
        }

        //获取class
        Class<?> aClass = allNode.get(0).getClass();
        //获取字典Field
        Map<String, Field> fieldMap = getAllFieldMap(aClass);
        //获取字段
        Field idField = fieldMap.get(idFieldStr);
        Field parentIdField = fieldMap.get(parentIdFieldStr);

        if (idField == null) {
            throw new IllegalArgumentException("找不到 " + idFieldStr + "属性");
        }
        if (parentIdField == null) {
            throw new IllegalArgumentException("找不到 " + parentIdFieldStr + "属性");
        }

        Map<Object, T> nodeMap = new HashMap<>();
        for (T i : allNode) {
            //idField.get(i)表示从对象i中获取指定字段的值
            nodeMap.put(idField.get(i), i);
        }

        Map<Object, String> allParentIdsMap = new HashMap<>();
        for (T i : allNode) {
            Object id = idField.get(i);
            Object parentId = parentIdField.get(i);
            String allParentIds = parentId.toString();

            while (true) {
                T t = nodeMap.get(parentId);
                if (Objects.isNull(t)) {
                    break;
                }
                parentId = parentIdField.get(t);
                allParentIds = parentId + "," + allParentIds;
            }

            String[] split = allParentIds.split(",");
            if (!split[0].equals(rootNodeParentIdValue.toString())) {
                throw new RuntimeException(idFieldStr + "为" + id + "的节点，所有的父节点id有缺失");
            }
            allParentIdsMap.put(id, allParentIds);
        }
        return allParentIdsMap;
    }


    /**
     * 获取当前节点下所有的子节点-递归方式
     *
     * @param idFieldStr       节点主键字段 例如id
     * @param parentIdFieldStr 节点父节点字段 例如parentId
     * @param allNode          节点数据 key为节点主键id值 value是当前节点
     * @param currentId        当前节点的主键 一般是主键id
     */
    public static <T> List<T> getCurrentNodeAllChildNode(
            String idFieldStr,
            String parentIdFieldStr,
            List<T> allNode,
            Object currentId
    ) throws IllegalAccessException {
        if (CollectionUtils.isEmpty(allNode) || Objects.isNull(currentId) || currentId.equals(0l) || currentId.equals(0)) {
            return new ArrayList<>();
        }

        //获取class
        Class<?> aClass = allNode.get(0).getClass();
        //获取字典Field
        Map<String, Field> fieldMap = getAllFieldMap(aClass);
        //获取字段
        Field idField = fieldMap.get(idFieldStr);
        Field parentIdField = fieldMap.get(parentIdFieldStr);

        if (idField == null) {
            throw new IllegalArgumentException("找不到 " + idFieldStr + "属性");
        }
        if (parentIdField == null) {
            throw new IllegalArgumentException("找不到 " + parentIdFieldStr + "属性");
        }

        List<T> list = new ArrayList<>();
        getCurrentNodeAllChildNodeRecursion(idField, parentIdField, list, currentId, allNode);

        return list;
    }

    public static <T> void getCurrentNodeAllChildNodeRecursion(Field idField, Field parentIdField, List<T> list, Object currentId, List<T> allNode) throws IllegalAccessException {

        for (T i : allNode) {
            Object id = idField.get(i);
            Object parentId = parentIdField.get(i);
            if (parentId.equals(currentId)) {
                list.add(i);
                getCurrentNodeAllChildNodeRecursion(idField, parentIdField, list, id, allNode);
            }
        }

    }

    /**
     * 获取当前节点下所有的子节点id
     *
     * @param idFieldStr       节点主键字段 例如id
     * @param parentIdFieldStr 节点父节点字段 例如parentId
     * @param allNode          节点数据 key为节点主键id值 value是当前节点
     * @param currentId        当前节点的主键 一般是主键id
     */
    public static <T> List<Object> getCurrentNodeAllChildNodeId(
            String idFieldStr,
            String parentIdFieldStr,
            List<T> allNode,
            Object currentId
    ) throws IllegalAccessException {
        List<T> nodes = getCurrentNodeAllChildNode(idFieldStr, parentIdFieldStr, allNode, currentId);
        if (CollectionUtils.isEmpty(nodes)) {
            return new ArrayList<>();
        }

        Class<?> aClass = allNode.get(0).getClass();
        Map<String, Field> fieldMap = getAllFieldMap(aClass);
        Field idField = fieldMap.get(idFieldStr);

        List<Object> ids = nodes.stream()
                .map(i -> {
                    try {
                        return idField.get(i);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                    return null;
                })
                .collect(Collectors.toList());

        return ids;

    }


    /**
     * 树每个层级的的排序
     * list 树节点数据
     * sortFieldStr 排序字段
     * childFieldStr 子类字段
     */
    public static <T> void treeSort(
            String sortFieldStr,
            String childFieldStr,
            List<T> treeList
    ) {
        if (CollectionUtils.isEmpty(treeList)) {
            return;
        }
        //获取class
        Class<?> aClass = treeList.get(0).getClass();
        //获取字典Field
        Map<String, Field> fieldMap = getAllFieldMap(aClass);
        //获取字段
        Field sortField = fieldMap.get(sortFieldStr);
        Field childField = fieldMap.get(childFieldStr);

        if (sortField == null) {
            throw new IllegalArgumentException("找不到 " + sortFieldStr + "属性");
        }
        if (childField == null) {
            throw new IllegalArgumentException("找不到 " + childFieldStr + "属性");
        }

        try {
            treeSort(sortField, childField, treeList);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    public static <T> void treeSort(
            Field sortField,
            Field childField,
            List<T> treeList
    ) throws IllegalAccessException {

        Collections.sort(treeList, Comparator.comparing(j -> {
            try {
                Object o = sortField.get(j);
                return (Integer) o;
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }));

        for (T i : treeList) {
            List<T> childList = (List<T>) childField.get(i);
            if (CollectionUtils.isEmpty(childList)) {
                continue;
            }
            treeSort(sortField, childField, childList);
        }
    }

    private static Map<String, Field> getAllFieldMap(Class<?> cls) {
        Field[] vFields = getAllFields(cls);
        Map<String, Field> fieldMap = new HashMap<>(vFields.length);
        for (Field field : vFields) {
            field.setAccessible(true);
            fieldMap.put(field.getName(), field);
        }
        return fieldMap;
    }

    private static Field[] getAllFields(final Class<?> cls) {
        final List<Field> allFieldsList = getAllFieldsList(cls);
        return allFieldsList.toArray(new Field[0]);
    }

    private static List<Field> getAllFieldsList(final Class<?> cls) {
        if (cls == null) {
            throw new IllegalArgumentException("The class must not be null");
        }
        final List<Field> allFields = new ArrayList<>();
        Class<?> currentClass = cls;
        while (currentClass != null) {
            final Field[] declaredFields = currentClass.getDeclaredFields();
            allFields.addAll(Arrays.asList(declaredFields));
            currentClass = currentClass.getSuperclass();
        }
        return allFields;
    }

    /**
     * 通过传入构建好的parentIds来判断是否会构成环结构，如果构成了环就返回true
     *
     * @param newParentIds parentIds
     * @return 有环true 没环false
     */
    public static boolean checkCircle(String newParentIds) {
        String[] id = newParentIds.split(",");
        Set<String> set = new HashSet<>();
        for (String s : id) {
            if (!set.add(s)) {
                return true;
            }
        }
        return false;
    }






}
